// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-7-13

#include "stdafx.h"
#include "mempool.h"
#include "thread.h"

//##ModelId=44CF1758000B
CJoyMemPool::CJoyMemPool(int nBlockStep, int nStepCount, int nGroupSize)
{
    m_nBlockStep = nBlockStep;
    switch(nBlockStep)
    {
    case 4   : m_nBlockStepShift = 2;break;
    case 8   : m_nBlockStepShift = 3;break;
    case 16  : m_nBlockStepShift = 4;break;
    case 32  : m_nBlockStepShift = 5;break;
    case 64  : m_nBlockStepShift = 6;break;
    case 128 : m_nBlockStepShift = 7;break;
    case 256 : m_nBlockStepShift = 8;break;
    case 512 : m_nBlockStepShift = 9;break;
    case 1024: m_nBlockStepShift = 10;break;
    case 2048: m_nBlockStepShift = 11;break;
    case 4096: m_nBlockStepShift = 12;break;
    default:
        ASSERT(FALSE);
    }
    
    m_nStepCount = nStepCount;
    m_nGroupSize = nGroupSize;
    m_nMaxAllocSize = m_nStepCount*m_nBlockStep;

    m_nMemTotal = 0;
    m_nMemUsing = 0;
    
    int blocksSize  = sizeof(CBlock*)*nStepCount;
    m_ppHeadBlocks  = (CBlock**)new char[blocksSize];
    memset(m_ppHeadBlocks, 0, blocksSize);
    m_pGroups       = NULL;
}

//##ModelId=44CF17580012
CJoyMemPool::~CJoyMemPool(void)
{
    ASSERT(m_nMemUsing == 0);

    while(m_pGroups != NULL)
    {
        CGroup* pGroup = m_pGroups;
        m_pGroups = m_pGroups->m_pNext;
        delete pGroup;
    }
    if(m_ppHeadBlocks != NULL)
    {
        delete m_ppHeadBlocks;
    }
}

//##ModelId=44CF17580015
void* CJoyMemPool::Alloc(size_t nSize)
{
    ++ m_nMemUsing;
    if(nSize > (UINT)m_nMaxAllocSize)
    {
        CBlock* pBlock = (CBlock*) new char[nSize+sizeof(CBlock)];
        pBlock->m_pNext = NULL;
        pBlock->m_ppHead= NULL;
        return pBlock+1;
    }

    size_t idx = (nSize-1) >> m_nBlockStepShift;
    CBlock* pHead = m_ppHeadBlocks[idx];
    if(pHead == NULL)
    {
        size_t s = (idx+1)<<m_nBlockStepShift;
        size_t b = s+sizeof(CBlock);
        size_t n = sizeof(CGroup*)+b*m_nGroupSize;
        CGroup* pGroup = (CGroup*)new BYTE[n];
        CBlock* pBlock = (CBlock*)(pGroup+1);
        CBlock* pEnd   = (CBlock*)((BYTE*)pGroup+n);
        //pBlock->m_pNext     = NULL;
        //m_ppHeadBlocks[idx] = pBlock;
        while(pBlock < pEnd)
        {
            pBlock->m_pNext = m_ppHeadBlocks[idx];
            m_ppHeadBlocks[idx] = pBlock;
            pBlock = (CBlock*)((BYTE*)pBlock+b);
        }
        pHead = (CBlock*)((char*)pEnd-b);
        m_nMemTotal += m_nGroupSize;
        pGroup->m_pNext = m_pGroups;
        m_pGroups = pGroup;
    }
    m_ppHeadBlocks[idx] = pHead->m_pNext;
    pHead->m_ppHead = m_ppHeadBlocks+idx;
    return pHead+1;
}

//##ModelId=44CF17580017
void CJoyMemPool::Free(void* pMem)
{
    ASSERT(pMem != NULL);
    -- m_nMemUsing;
    CBlock* pBlock = (CBlock*)pMem-1;
    CBlock** ppHead = pBlock->m_ppHead;
    if(ppHead == NULL)
    {
        delete (char*)pBlock;
        return;
    }

    pBlock->m_pNext = *ppHead;
    *ppHead = pBlock;
}

#ifndef JOY_DISABLE_POOL
#pragma push_macro("new")
#undef new

#pragma warning(disable : 4074)
#pragma warning(disable : 4073)
#pragma init_seg(lib)
static CJoyMemPool s_poolObj(32, 32, 16);
static CJoyCriticalSection  s_poolLock;

//##ModelId=44CF1758003E
void* CJoyPoolObject::Alloc(size_t s)
{
    CJoyAutoCS gate(s_poolLock);
    return s_poolObj.Alloc(s);
}

//##ModelId=44CF17580041
void CJoyPoolObject::Free(void* p)
{
    CJoyAutoCS gate(s_poolLock);
    s_poolObj.Free(p);
}

#pragma pop_macro("new")
#endif
